/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

/*!
  \file clientList.cxx
  \brief class implemenation XML parser

  \author Jack Elston
  $Date: 2005/02/12 00:20:45 $
*/

#include "clientList.h"

/*<---Extern Variables---->*/
extern in_addr netmask;

ClientList::ClientList()
{
	clear();
}

void ClientList::clearAddresses(int client_num) 
{
	for(int i =0; i<MAX_ADDRESSES; i++) {
		client_list[client_num].address[i].s_addr = INADDR_ANY;
		client_list[client_num].bcast_addr[i].s_addr = INADDR_ANY;
	}
}

bool ClientList::add(Client * a_client)
{
	//cout << "ClientList::add - client->name=" << a_client->name << endl;
	if( !a_client || a_client->name == "")
		return false;

	if(getClient(a_client->name) != NULL)
		return false;

	for(int i =0; i<MAX_CLIENTS; i++){
		if(client_list[i].name == "" && client_list[i].address[0].s_addr == INADDR_ANY) {
			client_list[i] = *a_client;
			num_clients ++;
			return true;
		}
	}
	return false;
}

bool ClientList::add(String name)
{
	//cout << "ClientList::add - name=" << name << endl;
	if( name == "" )
		return false;

	if(getClient(name) != NULL) {
		cout << "ClientList::add - client already exists" << endl;
		return false;
	}

	for(int i=0; i<MAX_CLIENTS; i++){
		if(client_list[i].name == "" && client_list[i].address[0].s_addr == INADDR_ANY) {
			client_list[i].name = name;
			num_clients ++;
			return true;
		}
	}
	cout << "ClientList::add - did not find empty slot for client " << endl;
	return false;
}

bool ClientList::addAddr(Client * a_client, in_addr address)
{
	in_addr bcast;
	bcast.s_addr = INADDR_ANY;

	return addAddr(a_client, address, bcast);
}

bool ClientList::addAddr(Client * a_client, in_addr address, in_addr bcast)
{

	if( !a_client )
		return false;

	if(getClient(a_client->name) == NULL)
		return false;

	int empty = -1;
	for(int i = 0; i<MAX_ADDRESSES; i++){
		if(a_client->address[i].s_addr == INADDR_ANY) {
			empty = i;
			break;
		}
		if(a_client->address[i].s_addr == address.s_addr)
			return false;
	}
	if( empty < 0 )
		return false;

	a_client->address[empty].s_addr = address.s_addr;
	a_client->bcast_addr[empty].s_addr = bcast.s_addr;

	//cout << "ClientList::addAddr - name=" << a_client->name;
	//cout << " addr=" << inet_ntoa(a_client->address[empty]); 
	//cout << " bcast=" << inet_ntoa(a_client->bcast_addr[empty]) << endl;
	
	return true;
}

in_addr ClientList::getAddr(Client * a_client)
{
	in_addr address;
	address.s_addr= INADDR_ANY;

	if( !a_client )
		return address;
	if(getClient(a_client->name) == NULL)
		return address;

	for(int i=0; i<MAX_ADDRESSES; i++){
		if(a_client->address[i].s_addr != INADDR_ANY) {
			address.s_addr = a_client->address[i].s_addr;
			break;
		}
	}
	return address;
}

in_addr ClientList::getBAddr(Client * a_client)
{
	in_addr address;
	address.s_addr= INADDR_ANY;

	if( !a_client )
		return address;
	if(getClient(a_client->name) == NULL)
		return address;

	for(int i =0; i<MAX_ADDRESSES; i++){
		if(a_client->bcast_addr[i].s_addr != INADDR_ANY) {
			address.s_addr = a_client->bcast_addr[i].s_addr;
			break;
		}
	}
	return address;
}

in_addr ClientList::getAddr(Client * a_client, Client * dest_client)
{
	in_addr address;
	address.s_addr= INADDR_ANY;
	in_addr_t subnet = getAddr(dest_client).s_addr & netmask.s_addr;

	if( !a_client || !dest_client )
		return address;
	if(getClient(a_client->name) == NULL)
		return address;

	for(int i =0; i<MAX_ADDRESSES; i++){
		if(a_client->address[i].s_addr != INADDR_ANY) {
			if(((a_client->address[i].s_addr) & netmask.s_addr) == subnet) {
				address.s_addr = a_client->address[i].s_addr;
				break;
			}
		}
	}
	return address;
}

bool ClientList::removeAddr(Client * a_client, in_addr address)
{
	if( !a_client )
		return false;
	if(getClient(a_client->name) == NULL)
		return false;
	for(int i =0; i<MAX_ADDRESSES; i++){
		if(a_client->address[i].s_addr == address.s_addr) {
			a_client->address[i].s_addr = INADDR_ANY;
			return true;
		}
	}
	return false;
}

bool ClientList::remove(Client * a_client)
{
	if( !a_client )
		return false;

	for(int i =0; i<MAX_CLIENTS; i++){
		if(client_list[i].name == a_client->name) {
			client_list[i].name = "";
			for(int j=0; j<MAX_NUM_CAP; j++)
				client_list[i].stream_subscriptions[j]=0;
			client_list[i].state.clear();
			clearAddresses(i);
			num_clients --;
			return true;
		}
	}
	return false;
}

bool ClientList::remove(String name)
{
	if( name == "" )
		return false;

	for(int i =0; i<MAX_CLIENTS; i++){
		if(client_list[i].name == name) {
			client_list[i].name = "";
			for(int j=0; j<MAX_NUM_CAP; j++)
				client_list[i].stream_subscriptions[j]=0;
			client_list[i].state.clear();
			clearAddresses(i);
			num_clients --;
			return true;
		}
	}
	return false;
}

void ClientList::clear()
{
	for(int i =0; i<MAX_CLIENTS; i++) {
		client_list[i].name = "";
		range[i] = NAN;
		clearAddresses(i);
		for(int j=0; j<MAX_NUM_CAP; j++)
			client_list[i].stream_subscriptions[j]=0;
		client_list[i].state.clear();
	}
	num_clients = 0;
}

Client * ClientList::getClient(int refnum)
{
	if( refnum < 0 && refnum >= MAX_CLIENTS )
		return NULL;

	if( client_list[refnum].name != "" )
		return &client_list[refnum];

	return NULL;
}

Client * ClientList::getClient(String name)
{
	if( name == "" )
		return NULL;

	for(int i = 0; i<MAX_CLIENTS; i++)
		if(client_list[i].name == name)  return &client_list[i];
	return NULL;
}

Client * ClientList::getClient(in_addr address)
{
	if( address.s_addr == INADDR_ANY)
		return NULL;

	for(int i=0; i<MAX_CLIENTS; i++) {
		for(int j=0; j<MAX_ADDRESSES; j++) {
			if(client_list[i].address[j].s_addr == address.s_addr) {
				return &client_list[i];
			}
		}
	}
	return NULL;
}

bool ClientList::isSubscribed(uint8_t id, int client)
{
  if( client >= MAX_CLIENTS || client < 0) return false;
  if( id == 0 || id == 255) return false;

  for(int j=0;j<MAX_NUM_CAP;j++) {
    if(client_list[client].stream_subscriptions[j] == id) {
      return true;
    }
  }

	return false;
}

Client * ClientList::getSubscriptionsTo(uint8_t id, int last_client)
{
	int current_client=0;
	for(int i=0; i<MAX_CLIENTS; i++) {
		for(int j=0; j<MAX_NUM_CAP; j++) {
			if(client_list[i].stream_subscriptions[j] == id) {
				current_client ++;
				if(current_client > last_client)
					return &client_list[i];
			}
		}
	}
	return NULL;
}

void ClientList::subscribe(Client * a_client, uint8_t a_system)
{
	int free=MAX_NUM_CAP;
	for(int i=0;i<MAX_NUM_CAP;i++)
	{
		if((a_client->stream_subscriptions[i]) == 0) {
			if(free >= MAX_NUM_CAP)
				free=i;
		}
		else if((a_client->stream_subscriptions[i]) == a_system)
		{
			free = -1;
			break;
		}
	}
	if((free >= 0) && (free < MAX_NUM_CAP)){
		//cout <<"ClientList::subscribe - " << a_client->name << " sys=" << (int)a_system << endl;
		(a_client->stream_subscriptions[free]) = a_system;
	}
}

void ClientList::unsubscribe(Client * a_client, uint8_t a_system)
{
	for(int i=0;i<MAX_NUM_CAP;i++)
	{
		if(a_system == 0)
			a_client->stream_subscriptions[i] = 0;
		else {
			if((a_client->stream_subscriptions[i]) == a_system)
			{
				a_client->stream_subscriptions[i] = 0;
				break;
			}
		}
	}
}

void ClientList::printList()
{
	cout << endl;
	cout << "     ----[ Clients ]-------------------------------------------------" << endl;
	for(int i = 0; i<MAX_CLIENTS; i++)
	{
		if(client_list[i].name != "") {
			cout << "       [+]  " <<  i << "   " << client_list[i].name << "   {";
			for(int j =0; j<MAX_ADDRESSES; j++)
				if(client_list[i].address[j].s_addr != INADDR_ANY)
					cout << " " << inet_ntoa(client_list[i].address[j]);
			cout << " }" << endl;
			client_list[i].capabilities.printCapabilities();
			cout << "        |-> SUBS ";
			for(int j=0; j<MAX_NUM_CAP; j++)
			{
				if((client_list[i].stream_subscriptions[j]) != 0)
					cout << hex << (int)(client_list[i].stream_subscriptions[j]) << dec << " " << flush;
			}
			cout << endl;
		}
	}
	cout << "     ----------------------------------------------------------------";
	cout << endl << endl;
}

int ClientList::getRefNum(Client *client)
{
	if( !client )
		return -1;

	int c_ind = 0;
	while( client_list[c_ind].name != "" )
	{
		// see if we found the client
		if( client_list[c_ind].name == client->name )
			return c_ind;

		// next client
		c_ind++;
	}

	return -1;
}

void ClientList::setRange(Client *client)
{
	if( !client )
		return;

	int c_ind = getRefNum(client);
	if( c_ind < 0 || c_ind >= MAX_CLIENTS )
		return;

	range[c_ind] = client_list[0].rangeTo(client);
}

void ClientList::setRange(int refnum)
{
	if( refnum < 0 || refnum >= MAX_CLIENTS )
		return;

	Client *a_client = getClient(refnum);
	if(a_client)
		range[refnum] = client_list[0].rangeTo(a_client);
}

float ClientList::getRange(Client *client)
{
	if( !client )
		return NAN;

	int c_ind = getRefNum(client);
	return getRange(c_ind);
}

float ClientList::getRange(int refnum)
{
	if( refnum < 0 || refnum >= MAX_CLIENTS )
		return NAN;

	return range[refnum];
}
